package com.mz.mattress.client.service.impl;

import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.mz.mattress.api.redis.RedisKeys;
import com.mz.mattress.api.common.Result;
import com.mz.mattress.api.entity.*;
import com.mz.mattress.api.req.PrepayWxReq;
import com.mz.mattress.api.util.IdUtil;
import com.mz.mattress.api.util.JacksonUtils;
import com.mz.mattress.api.redis.RedisLock;
import com.mz.mattress.api.util.SpringUtil;
import com.mz.mattress.api.wx.WxProperties;
import com.mz.mattress.client.mapper.OrderMapper;
import com.mz.mattress.client.service.PayService;
import com.mz.mattress.client.service.WxPayRecordService;
import com.mz.mattress.client.service.WxRefundRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public abstract class AbstractPayService implements PayService {

    @Autowired
    private RedisLock redisLock;

    @Autowired
    protected WxProperties wxProperties;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private WxPayRecordService wxPayRecordService;

    @Autowired
    private WxRefundRecordService wxRefundRecordService;

    @Autowired
    private OrderMapper orderMapper;

    @Override
    public Result<Object> prepay(PrepayWxReq req) {

        Result valid = beforePrepay(req);
        if (valid.isFail()) {
            return valid;
        }

        String orderIdStr = req.getOrderId();

        Long orderId = Long.valueOf(orderIdStr);

        String outTradeNo = IdUtil.generateSsn();

        orderMapper.updateOutTradeNo(orderId, outTradeNo);

        WxPayMpOrderResult result = null;
        try {
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            request.setOutTradeNo(outTradeNo);
            request.setBody(bodyKey + orderIdStr);
            request.setDetail(bodyKey + orderIdStr);
            request.setOpenid(req.getOpenId());
            if (SpringUtil.isProd()) {
                request.setTotalFee(req.getPayMoney());
            } else {
                request.setTotalFee(1);
            }
            request.setSpbillCreateIp(req.getIp());
            request.setNotifyUrl(getPayNotifyUrl(orderIdStr));
            request.setTradeType(WxPayConstants.TradeType.JSAPI);
            result = wxPayService.createOrder(request);
        } catch (WxPayException e) {
            e.printStackTrace();
            log.error("submit_error", e);
        }

        return Result.ok(result);
    }

    @Override
    public Result<Integer> queryIsPaid(Long orderId, String outTradeNo) {

        String redisKey = RedisKeys.getOrderKey(orderId.toString());
        Boolean isLock = redisLock.tryLock(redisKey, REQUEST_ID, 5, TimeUnit.SECONDS);
        if (!isLock) {
            log.info("get_redis_key_fail=" + redisKey);
            return Result.fail();
        }

        try {

            if (checkIsPaid(orderId)) {
                return Result.ok(1);
            }

            if (outTradeNo == null) {
                Order order = orderMapper.selectByOrderId(orderId);
                outTradeNo = order.getOutTradeNo();
            }

            if (isPaySuccess(outTradeNo, orderId.toString())) {
                doPaySuccess(orderId);
                return Result.ok(1);
            }
            return Result.ok(0);
        } catch (Exception e) {
            log.error("queryIsPaid_error", e);
            return Result.fail();
        } finally {
            redisLock.releaseLock(redisKey, REQUEST_ID);
        }
    }

    @Override
    public Result doRefundMoney(Long orderId, Integer money) {

        String redisKey = RedisKeys.getOrderKey(orderId.toString());
        Boolean isLock = redisLock.tryLock(redisKey, "1", 10, TimeUnit.SECONDS);
        if (!isLock) {
            log.warn("getOrderKey_fail_doRefund," + redisKey);
            return Result.fail();
        }

        try {

            Result<Long> result = checkIsCanRefund(orderId, money);
            if (!result.isSuccess()) {
                return result;
            }

            Long refundId = result.getResult();

            List<WxPayRecord> records = wxPayRecordService.selectByOrderId(orderId);
            if (records.isEmpty()) {
                return Result.fail("没有支付记录");
            }

            WxPayRecord record = records.get(0);

            if (money > record.getTotalFee()) {
                return Result.fail("退款金额不能超过支付金额");
            }

            WxPayRefundRequest req = new WxPayRefundRequest();
            req.setOutTradeNo(record.getOutTradeNo());    // 商户订单号
            req.setOutRefundNo(refundId.toString()); // 退款单号
            req.setTotalFee(record.getTotalFee());   // 订单总金额
            req.setRefundFee(record.getTotalFee()); // 退款金额
            req.setNotifyUrl(getRefundNotifyUrl(orderId.toString()));
            WxPayRefundResult refund = wxPayService.refund(req);
            wxRefundRecordService.insert(refund);

            if (SUCCESS.equals(refund.getReturnCode()) && SUCCESS.equals(refund.getReturnCode())) {
                doRefundSuccess(orderId, money);
                log.info("update_refund_money,orderId={},refund={}", orderId, money);
            }
            return Result.ok();

        } catch (WxPayException e) {
            return Result.fail(e.getMessage());
        } finally {
            redisLock.releaseLock(redisKey, REQUEST_ID);
        }
    }

    private String getPayNotifyUrl(String orderId) {
        return wxProperties.getPayNotifyUrl() + orderId;
    }

    private String getRefundNotifyUrl(String orderId) {
        return wxProperties.getRefundNotifyUrl() + orderId;
    }

    public abstract Result beforePrepay(PrepayWxReq prepayWxReq);

    //检查订单是否已支付  保持接口面等
    public abstract boolean checkIsPaid(Long orderId);

    /**
     * 对支付信息，做后续处理  检查是否已支付足额金额，然后发货
     * @param orderId
     */
    public abstract void doPaySuccess(Long orderId);

    public abstract void doRefundSuccess(Long orderId, Integer money);

    /**
     * V2接口验证微信支付通知参数
     * @return
     */
    public boolean verifyWxPayParams(WxPayOrderNotifyResult result,
                                     Integer payMoney) {

        try {
            result.checkResult(wxPayService, WxPayConstants.SignType.MD5, true);
        } catch (Exception e) {
            log.error("wx_checkResult_error", e);
            return Boolean.FALSE;
        }

        // 核对金额
        Integer totalFee = result.getTotalFee();            // 总金额
        log.info("total_fee=" + totalFee + ",payMoney=" + payMoney);

        if (payMoney.compareTo(totalFee) != 0) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    private boolean isPaySuccess(String outTradeNo, String orderId) {

        WxPayOrderQueryResult result = null;
        try {
            WxPayOrderQueryRequest req = new WxPayOrderQueryRequest();
            req.setOutTradeNo(outTradeNo);
            result = wxPayService.queryOrder(req);
            log.info("pay_result=" + JacksonUtils.toJson(result));
        } catch (WxPayException e) {
            log.error("isPaySuccess_error", e);
            return false;
        }

        if (SUCCESS.equals(result.getTradeState())) {
            wxPayRecordService.insert(result, orderId);
            return true;
        }

        return false;

        //支付成功
//        return SUCCESS.equals(result.getTradeState());
    }


    /**
     * 检查是否可退款
     * @param orderId
     * @return 退款订单号
     */
    public abstract Result<Long> checkIsCanRefund(Long orderId, Integer money);

    @Override
    public void payNotify(WxPayOrderNotifyResult result, String orderId) {

        String orderIdStr = result.getOutTradeNo();

        String redisKey = RedisKeys.getOrderKey(orderIdStr);
        Boolean isLock = redisLock.tryLock(redisKey, REQUEST_ID, 5, TimeUnit.SECONDS);
        if (!isLock) {
            log.info("get_redis_key_fail=" + redisKey);
            return;
        }
        log.info("start_processCallback_redisKey=" + redisKey);
        try {

            if (checkIsPaid(Long.valueOf(orderIdStr))) {
                return;
            }

            wxPayRecordService.insert(result, orderIdStr);

//            Integer money = order.getMoney();
//            if (money != null && verifyWxPayParams(result, money)) {
//                Order update = new Order();
//                update.setId(order.getId());
//                update.setPayMoney(money);
//                update.setStatus(OrderStatusEnum.PAID.getValue());
//                orderMapper.updateById(update);
//
//                memberScoreService.sendScore(order.getMemberId(), order.getId(), money);
//            }
        } catch (Exception e) {
            log.error("callback_check_error", e);
        } finally {
            redisLock.releaseLock(redisKey, REQUEST_ID);
        }

    }

    @Override
    public void refundNotify(WxPayRefundNotifyResult result, String orderId) {

//        String orderIdStr = result.gettr
//
//        String redisKey = RedisKeys.getOrderKey(orderIdStr);
//        Boolean isLock = redisLock.tryLock(redisKey, REQUEST_ID, 5, TimeUnit.SECONDS);
//        if (!isLock) {
//            log.info("get_redis_key_fail=" + redisKey);
//            return;
//        }
//        log.info("start_processCallback_redisKey=" + redisKey);
//        try {
//
//            if (checkIsPaid(Long.valueOf(orderIdStr))) {
//                return;
//            }
//
//            wxRefundRecordService.insert(result, orderIdStr);

//            Integer money = order.getMoney();
//            if (money != null && verifyWxPayParams(result, money)) {
//                Order update = new Order();
//                update.setId(order.getId());
//                update.setPayMoney(money);
//                update.setStatus(OrderStatusEnum.PAID.getValue());
//                orderMapper.updateById(update);
//
//                memberScoreService.sendScore(order.getMemberId(), order.getId(), money);
//            }
//        } catch (Exception e) {
//            log.error("callback_check_error", e);
//        } finally {
//            redisLock.releaseLock(redisKey, REQUEST_ID);
//        }

    }
}
